﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data.SqlClient;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using PhotgalleryEntities;
using PhotoGalleryEnteties;
using System.Data.SqlTypes;

namespace EntityProvider
{
    public class FilestreamPhotoStorage:IPhotoManager
    {

        private readonly int _optimizedImageWidth=600;
        private readonly int _thumbWidth=100 ;
        private string _connectionString;


        private IScaler _photoScaler;
        public IScaler PhotoScaler { get { return _photoScaler; } }



        public FilestreamPhotoStorage(IScaler scaler)
            : this("ApplicationServices", scaler)
        {
            
        }

        public FilestreamPhotoStorage(string ConnectionStringName, IScaler photoScaler)
        {

            _connectionString = ConfigurationManager.ConnectionStrings[ConnectionStringName].ConnectionString;
            _photoScaler = photoScaler;
        }


        public FilestreamPhotoStorage (string ConnectionStringName,IScaler photoScaler,int optimizedImageWidth,int thumbWidth)
        {
           
            _connectionString= ConfigurationManager.ConnectionStrings[ConnectionStringName].ConnectionString;
            _photoScaler = photoScaler;
            _thumbWidth = thumbWidth;
            _optimizedImageWidth = optimizedImageWidth;
        }
        
        
      

        
        /// <summary>
        /// method for saving photo in filestream storage photos are saved in original format optimized anf thumbnail in jpeg 
        /// </summary>
  
        public void SavePhoto(PhotoInfo photoInfo,Bitmap image)
        {
            
          


                   //inserting original photo
                   using (FilestreamPhotoTransaction optTrans=new FilestreamPhotoTransaction( _connectionString ,photoInfo.PhotoId ,PhotoType.OriginalImage))
                   {
                       using (Stream  originalStream = optTrans .ResultStream )
                       {
                           image.Save(originalStream, ImageFormat.Jpeg);
                           originalStream.Close();
                       }

                       optTrans.CommitTransaction();
                   }
                   //scailing original photo to optimal size and inserting
                   if(image.Width >_optimizedImageWidth )
                   {
                       using (FilestreamPhotoTransaction optTrans = new FilestreamPhotoTransaction(_connectionString, photoInfo.PhotoId, PhotoType.OptimizedImage ))
                       {
                           using (Stream optimizedStream = optTrans.ResultStream )
                           {

                             
                                   // calculating optimized image height
                                   int newHeight = Convert.ToInt32(image.Height * ((decimal)_optimizedImageWidth) / image.Width);
                                   //image scaling and saving
                                   Bitmap scaledImage = _photoScaler.ResizeImage(image, _optimizedImageWidth, newHeight);
                                   scaledImage.Save(optimizedStream, ImageFormat.Jpeg);

                               
                           }
                           optTrans.CommitTransaction();
                       }

                   }

                   //scaling image to thumbnail and saving 
                   if(image.Width>_thumbWidth || image.Height >_thumbWidth  )
                   {
                       using (FilestreamPhotoTransaction thumbTrans = new FilestreamPhotoTransaction(_connectionString, photoInfo.PhotoId, PhotoType.ImageThumbnail))
                       {
                           using (Stream  thumbnailStream = thumbTrans.ResultStream )
                           {

                               //calculating thumb height
                               int thumbWidth;
                               int thumbHeight;
                               if(image.Height >image.Width)
                               {
                                   thumbHeight = 100;
                                   thumbWidth = (int) Math.Ceiling((decimal) thumbHeight/image.Height*image.Width);
                               }
                               else
                               {
                                   thumbWidth = 100;
                                   thumbHeight = (int) Math.Ceiling((decimal) thumbWidth/image.Width*image.Height);
                               }

                               Bitmap imageThumbnail = new Bitmap(image.GetThumbnailImage(thumbWidth ,thumbHeight , delegate { return false; }, new IntPtr()));
                               imageThumbnail.Save(thumbnailStream, ImageFormat.Jpeg);
                               
                           }
                           thumbTrans.CommitTransaction();
                       }
                   }
 
           
        }

     
        public IStorageResult  LoadOriginalPhoto(PhotoInfo photoInfo )
        {
           

            return new FilestreamPhotoTransaction( _connectionString ,photoInfo.PhotoId,PhotoType.OriginalImage);
        }


        public IStorageResult LoadOptimizedPhoto(PhotoInfo photoInfo)
        {

            return new FilestreamPhotoTransaction(_connectionString, photoInfo.PhotoId, photoInfo.Width <= _optimizedImageWidth ? PhotoType.OriginalImage : PhotoType.OptimizedImage );
        }

        public IStorageResult LoadThumbnail(PhotoInfo photoInfo)
        {

            return new FilestreamPhotoTransaction(_connectionString, photoInfo.PhotoId, photoInfo.Width <= _thumbWidth  ? PhotoType.OriginalImage : PhotoType.ImageThumbnail );

        }

      


        /// <summary>
        /// Filestream context class.
        /// </summary>
        public class FilestreamPhotoTransaction:IStorageResult
        {
            private SqlConnection _connection;

            private SqlTransaction _transaction;

            private int _photoId;

            private byte[] _transactionContext;

            private bool _isFinished = false;

            private SqlFileStream _resultStream;


            
            /// <summary>
            /// Constructor .Opens connection with db, starts transaction , and oprns stream for reading from db 
            /// </summary>
            /// <param name="connectionString"> Connection string to filestream db</param>
            /// <param name="photoId"> Specified photo id </param>
            /// <param name="type"> specifies phototype assosiated with transaction</param>
            public FilestreamPhotoTransaction(string connectionString ,int photoId,PhotoType type)
            {
                _connection = new SqlConnection(connectionString);
                _connection.Open();
                _transaction = _connection.BeginTransaction("NewTransaction");
                _photoId = photoId;
                SqlCommand command = new SqlCommand()
                                         {
                                             CommandText = "SELECT GET_FILESTREAM_TRANSACTION_CONTEXT()",
                                             Transaction = _transaction,
                                             Connection = _connection
                                         };
                _transactionContext = (byte[]) command.ExecuteScalar();

                 command = new SqlCommand()
                {
                    Connection = _connection,
                    CommandText =
                        "select " + type.ToString() +
                        ".PathName() from photogallery.dbo.Photos where PhotoId=" + _photoId,
                    Transaction = _transaction
                };
                string path;
                object result = command.ExecuteScalar();
                if (DBNull.Value == result)
                {

                    SqlCommand anotherCommand = new SqlCommand()
                    {
                        Connection = _connection,
                        CommandText =
                            "update Photos set " + type + " =0x0 where PhotoId =" +
                            _photoId,
                        Transaction = _transaction
                    };
                    anotherCommand.ExecuteNonQuery();
                    result = command.ExecuteScalar();

                }
                path = (string)result;
                _resultStream = new SqlFileStream(path, _transactionContext, FileAccess.ReadWrite);



            }

            public Stream ResultStream { get { return _resultStream; } }

          

            public void CommitTransaction()
            {
                _transaction.Commit();
                _isFinished = true;
            }

            public void RollbackTransaction()
            {
                _transaction.Rollback();
                _isFinished = true;
            }

            public void Dispose()
            {
                if (!_isFinished)
                    _transaction.Rollback();
                _connection.Close();
            }







        }

        public class FilestreamStorageException:Exception
        {
            public FilestreamStorageException (string message):base(message )
            {
                
            }
        } 
    }
}
